/*
 * kernel/sched.c
 *
 * last modified by ChenJie at 2007.07.15
 *
 */
#include <clinux/kernel.h>
#include <clinux/sched.h>
#include <clinux/mm.h>
#include <clinux/time.h>
#include <asm/i8259.h>
#include <asm/i8253.h>
#include <clinux/interrupt.h>
#include <cos/protect.h>
#include <cos/ipc.h>

struct procs_struct procs;


static void set_init_proc(void)
{
	//设置进程基本信息：
	init_proc.proc.counter = TASK_INIT_COUNTER;
	init_proc.proc.status = TASK_RUNNING;
	init_proc.proc.priority = TASK_KERNEL_PRIORITY;
	init_proc.proc.task_nr = 0;
	init_proc.proc.ktime = 0;
	init_proc.proc.utime = 0;
	init_proc.proc.next = 0;
	init_proc.proc.pid = 0;
	init_proc.proc.reenter = 0;
	init_proc.proc.start_time = procs.jiffies;
	init_proc.proc.ulevel = 1;

	//设置进程LDT表：
	/*
	 * 注意这里task0的ring3级被限制在0~640KB范围内
	 */
	set_seg_desc(&(init_proc.proc.ldt.kstack),	// kernel_SS
			DA_DRW|DA_32|DA_LIMIT_1B|DA_DPL0,
			&init_proc.kstack, 4096);
	set_seg_desc(&(init_proc.proc.ldt.ucode),	//USER_CS
			DA_CR  |DA_32|DA_LIMIT_4K|DA_DPL3, 
			TASK_TEXT_START, (TASK0_TEXT_LIMIT)>>12);
	set_seg_desc(&(init_proc.proc.ldt.udata),		//USER_DS
			DA_DRW|DA_32|DA_LIMIT_4K|DA_DPL3,
			TASK_DATA_START, (TASK0_DATA_LIMIT)>>12);
	set_seg_desc(&(init_proc.proc.ldt.ustack),	//USER_SS
			DA_DRW|DA_32|DA_LIMIT_4K|DA_DPL3,
			TASK_STACK_START, (TASK0_STACK_LIMIT)>>12);
	//设置TSS(仅设置动态字段集)：
	init_proc.proc.tss.back_link = 0;
	init_proc.proc.tss.esp0 = (unsigned long)(&(init_proc.kstack))+4096;
	init_proc.proc.tss.ss0 = SELECTOR_KDS;
	init_proc.proc.tss.esp1 = 0;
	init_proc.proc.tss.ss1 = 0;
	init_proc.proc.tss.esp2 = 0;
	init_proc.proc.tss.ss2 = 0;
	init_proc.proc.tss.cr3 = (unsigned long)pg_dir;
	init_proc.proc.tss.es  = 0;
	init_proc.proc.tss.cs  = 0;
	init_proc.proc.tss.ss  = 0;
	init_proc.proc.tss.ds  = 0;
	init_proc.proc.tss.fs  = 0;
	init_proc.proc.tss.gs  = 0;
	init_proc.proc.tss.ldt = TASK_LDT_SELECTOR(0);
	init_proc.proc.tss.trace_bitmap = 0;
	//init_task.task.tss.i387 = {0,0...};

	//设置GDT中的本任务的LDT描述符和TSS描述符：
	set_tss_desc(&gdt[TASK_TSS_ENTRY(0)], &init_proc.proc.tss);
	set_ldt_desc(&gdt[TASK_LDT_ENTRY(0)], &init_proc.proc.ldt);

	//设置映射页表：(直接使用内核页面)
	copy_kernel_pages_to_user(0, 0xA0000, TASK_TEXT_START);
	copy_kernel_pages_to_user(0, 0xA0000, TASK_DATA_START);
	copy_kernel_pages_to_user(0, 0xA0000, TASK_STACK_START);
/*		pg_dir[(TASK_TEXT_START>>22)] = pg_dir[0];
		pg_dir[(TASK_DATA_START>>22)] = pg_dir[0];
		pg_dir[(TASK_STACK_START>>22)] = pg_dir[0];
*/
	//在进程表中注册该任务：
	procs.task[0] = &init_proc.proc;
	
	//加载LDT和TSS段描述符选择子：
	lldt(TASK_LDT_SELECTOR(0));
	ltr(TASK_TSS_SELECTOR(0));
	//刷新cr3:
	set_cr3(pg_dir);
}


long enqueue(struct proc_struct * proc_ptr)
{
	//check if the proc_ptr's priority is validate:
	if(proc_ptr->priority < 0 || proc_ptr->priority >= NR_PRIORITY) 
		return -1;//fail
	
	if(procs.ready_tail[proc_ptr->priority]==0){
		// current queue-list is empty, so make the head and tail point to the proc:
		procs.ready_head[proc_ptr->priority] = proc_ptr;
		procs.ready_tail[proc_ptr->priority] = proc_ptr;
		proc_ptr->next = 0;
	}else{
		// add the new proc to the tail of the queue-list:
		procs.ready_tail[proc_ptr->priority]->next = proc_ptr;
		proc_ptr->next = 0;
		procs.ready_tail[proc_ptr->priority] = proc_ptr;
	}
	return 0; //successful
}
long dequeue(struct proc_struct * proc_ptr)
{
	struct proc_struct *p;
	//check if the proc_ptr's priority is validate:
	if(proc_ptr->priority < 0 || proc_ptr->priority >= NR_PRIORITY) 
		return -1;//fail
		
	// find the the pointer of proc which is  before proc_ptr:
	p = procs.ready_head[proc_ptr->priority];
	
	if(p==0)	return -1;//success
	
	if(p==proc_ptr){
		procs.ready_head[proc_ptr->priority] = proc_ptr->next;
		if(procs.ready_tail[proc_ptr->priority]==proc_ptr) {
			procs.ready_tail[proc_ptr->priority] = 0;
		}
		return 0; //successful
	}
	//the first proc of the list is not the proc_ptr, then ...
	while(p->next!=0 && p->next != proc_ptr) p=p->next;

	// check if the proc_ptr has found:
	if(p->next == 0) return -1; //fail
	
	// remove the proc_ptr from the queue-list:
	p->next = proc_ptr->next;
	proc_ptr->next = 0;

	// check if the proc_ptr is the tail of the queue:
	if(procs.ready_tail[proc_ptr->priority]==proc_ptr) 
		procs.ready_tail[proc_ptr->priority] = p;

	return 0; //success
}

void check_schedule(void)
{
	if(procs.run_proc->status != TASK_RUNNING 
		|| procs.run_proc->counter == 0){
		schedule();
	}
}

void schedule(void)
{
	int i;
	for(i=0; i<NR_PRIORITY; i++){
		// check if current priority-list has ready task:
		if(procs.ready_head[i]!=0) break;
	}
	proc_switch_to(procs.ready_head[i]);
}

void sleep_on(struct proc_struct ** event)
{
	dequeue(procs.run_proc);
	// change the status of the proc:
	procs.run_proc->status = TASK_INTERRUPTIBLE;
	// add to the waiting list:
	procs.run_proc->next = (*event)->next;
	*event = procs.run_proc;

	// give up the cpu:
	schedule();
}

long wake_up(struct proc_struct ** event, long pid)
{
	struct proc_struct * proc_ptr;
	struct proc_struct ** p;
	
	if(*event == 0) return -1;
	
	if(pid == ANY_PROC) {
		// remove the first waiting proc of the waiting list:
		proc_ptr = *event;
		*event = proc_ptr->next;
		proc_ptr->next = 0;
		//
		proc_ptr->status = TASK_RUNNING;
		enqueue(proc_ptr);
		
	}else if(pid == ALL_PROC){
		while(*event!=0) {
			// remove the first waiting proc of the waiting list:
			proc_ptr = *event;
			*event = proc_ptr->next;
			proc_ptr->next = 0;
			//
			proc_ptr->status = TASK_RUNNING;
			enqueue(proc_ptr);
		}
	}else if(pid >= 0){
		p = event;
		while((*p!=0) &&((*p)->pid != pid))  p = &((*p)->next);
		if(*p == 0) return -1;
		// remove the first waiting proc of the waiting list:
		proc_ptr = *p;
		*p = proc_ptr->next;
		proc_ptr->next = 0;
		//
		proc_ptr->status = TASK_RUNNING;
		enqueue(proc_ptr);	
	}
	return 0; //success
}
void timeout()
{
	if(procs.run_proc->priority > 4
		&&  procs.run_proc->priority < NR_PRIORITY-1) {
		dequeue(procs.run_proc);
		if(++procs.run_proc->priority == NR_PRIORITY-1) 
			procs.run_proc->priority = 5;
		enqueue(procs.run_proc);
	}
	schedule();
}

long do_clock_interrupt(struct irq_hook * hook)
{
	// check if the hook is clock's hook:
	if(hook->irq!=IRQ_CLOCK) return -1;

	// increase the jiffies:
	procs.jiffies ++;

	// send EOI to the pic:
	SEND_MASTER_EOI();

	if(procs.run_proc->reenter == 1){
		procs.run_proc->utime ++;
	}else{
		procs.run_proc->ktime ++;
	}
	// current proc's counter --
	procs.run_proc->counter--;
	
	// check if the proc's counter is use up...
	if(procs.run_proc->counter > 0) return 0;
	
	showtime();
	xycprintk(2,0,TEXT_FR_I|TEXT_FR_G,
			"[%ld]: jiffies = %ld, priority=%d, ktime=%d, utime=%d, reenter=%d", 
			procs.run_proc->pid, procs.jiffies,  procs.run_proc->priority,
		procs.run_proc->ktime, 
		procs.run_proc->utime, procs.run_proc->reenter);
	
	// the proc's time has use up:
	procs.run_proc->counter = 20;
	
	// if the proc is running in the kernel-mode before the clock-interrupt, then don't schedule:
	if(procs.run_proc->reenter != 1) return 0;//工作在内核态时，直接返回

	timeout();
	
	return 0;	
}
/*
void do_clock_interrupt0(int cpl)
{
	static unsigned long times=0;
	showtime();
	xycprintk(50,24,TEXT_FR_I|TEXT_FR_G,
			"[system]: jiffies = %ld, pid=%d", 
			jiffies, current_task->pid);
	if(cpl==0) return;//工作在内核态时，直接返回
	times++;
	//xycprintk(158,(times-1)%25,TEXT_FR_I|TEXT_FR_G," ");
	xycprintk(158,times%25,TEXT_FR_I|TEXT_FR_G,"%d", current_task->pid);
	schedule();
}

*/
void sched_init(void)
{
	int i;
	procs.jiffies = 0;
	procs.last_pid = 0;
	procs.last_proc_used_math = 0;
	procs.run_proc = 0;
	for(i=0; i<NR_PRIORITY; i++){
		procs.ready_head[i] = 0;
		procs.ready_tail[i] = 0;
	}
	for(i=0;i<NR_TASKS; i++){
		procs.task[i] = 0;
	}
	set_init_proc();
	enqueue(&init_proc.proc);
	procs.run_proc= &init_proc.proc;
	//复位NT标志位
	__asm__("pushfl; andl $0xffffbfff, (%esp); popfl");

	irq_put_hook(IRQ_CLOCK, do_clock_interrupt, 0);
	/* 
	 * 设置时钟中断的产生速率是100Hz/s
	 */
	i8253_init(0,3,LATCH);
	enable_irq_clock();
}
